////////////////////////////////////////////////////////////////////////////
//                            **** LZW-AB ****                            //
//               Adjusted Binary LZW Compressor/Decompressor              //
//                  Copyright (c) 2016-2020 David Bryant                  //
//                           All Rights Reserved                          //
//      Distributed under the BSD Software License (see license.txt)      //
////////////////////////////////////////////////////////////////////////////

#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "xsdps.h"		/* SD device driver */
#include "ff.h"
#include "lzwlib.h"

/* This module provides a command-line test harness for the lzw library.
 * Given a list of files, it will read each one and byte-for-byte verify
 * the data after a round-trip through a compression / decompression cycle
 * at each of the 8 available maximum symbol size settings.
 *
 * It can also optionally perform fuzz testing by randomly corrupting the
 * compressed bitstream. Obviously this will introduce integrity failures,
 * but it should not cause a crash. It also has an "exhaustive" mode that
 * creates hundreds of simulated images from each input file by successive
 * truncation from both ends.
 */

typedef struct
{
    unsigned int size, index, wrapped, byte_errors, first_error, fuzz_testing;
    unsigned char *buffer;
} streamer;

static int read_buff(void *ctx)
{
    streamer *stream = ctx;

    if(stream->index == stream->size)
        return EOF;

    return stream->buffer[stream->index++];
}

static void write_buff(int value, void *ctx)
{
    streamer *stream = ctx;

    // for fuzz testing, randomly corrupt 1 byte in every 65536 (on average)

    if(stream->fuzz_testing) {
        static unsigned long long kernel = 0x3141592653589793;
        kernel = ((kernel << 4) - kernel) ^ 1;
        kernel = ((kernel << 4) - kernel) ^ 1;
        kernel = ((kernel << 4) - kernel) ^ 1;

        if(!(kernel >> 48))
            value ^= (int)(kernel >> 40);
    }

    if(stream->index == stream->size) {
        stream->index = 0;
        stream->wrapped++;
    }

    stream->buffer[stream->index++] = value;
}

int lzw()
{
    int checked = 0, tests = 0, skipped = 0, errors = 0;
    streamer reader, writer;

    memset(&reader, 0, sizeof(reader));
    memset(&writer, 0, sizeof(writer));

    const char* compress_filename = "0:/BOOT_lzw.BIN";
    const char* restore_filename = "0:/BOOT.BIN";
    unsigned char *file_buffer;
    int file_size = 0;
    static FIL infile;
    FRESULT Res;

    UINT NumBytesRead;
    UINT NumBytesWritten;
    int FW_maxsize = 32 * 1024 * 1024;	//�̼����ߴ磬�ݶ�30MB

    /***********************compress function begin*************************/

    Res = f_open(&infile, compress_filename, FA_READ | FA_OPEN_EXISTING);

    if(Res) {
       // printf("\ncan't open file %s!\n", compress_filename);
        skipped++;
        return XST_FAILURE;
    }

    file_size = f_size(&infile);

    if(!file_size) {
       // printf("\ncan't get file size of %s (may be zero)!\n", compress_filename);
        skipped++;
        return XST_FAILURE;
    }

    if(file_size > 10LL * 1024LL * 1024LL) {
      //  printf("\nfile %s is too big!\n", compress_filename);
        skipped++;
        return XST_FAILURE;
    } else {
     //   printf("\nSize of file %s is %d!\n", compress_filename, file_size);
    }

    file_buffer = (unsigned char *)malloc(file_size);
    writer.size = (unsigned int)(FW_maxsize);
    writer.buffer = (unsigned char *)malloc(FW_maxsize);

    if(!file_buffer || !writer.buffer) {
    //    printf("\nfile %s is too big!\n", compress_filename);

        if(writer.buffer) free(writer.buffer);
        if(file_buffer) free(file_buffer);
        skipped++;
        return 1;
    }

    Res = f_read(&infile, (void*)file_buffer, (int)file_size, &NumBytesRead);

    if(Res != FR_OK) {
     //   printf("\ncan't read file %s!\n", compress_filename);
        return XST_FAILURE;
    } else {
     //   printf("\nSuccessfully read file %s, size is %dByte\n", compress_filename, NumBytesRead);
    }

    f_close(&infile);

    checked++;

    do{
        reader.buffer = file_buffer;
        reader.size = NumBytesRead;
        reader.index = writer.index = writer.wrapped = 0;

        /***********************decompress function begin*********************/

        if(lzw_decompress(write_buff, &writer, read_buff, &reader)) {
       //     printf("\nlzw_decompress() returned error on file %s\n", restore_filename);
            errors++;
            return XST_FAILURE;
        } else
       //     printf("\n Successfully ran lzw_decompress() !\n");

        if(writer.wrapped) {
       //     printf("\nover 100%% inflation on file %s\n", restore_filename);
            errors++;
        }

        Res = f_open(&infile, restore_filename, FA_WRITE | FA_READ | FA_CREATE_ALWAYS);

        if(Res) {
       //     printf("\ncan't open file %s!\n", restore_filename);
            skipped++;
            return XST_FAILURE;
        }

        Res = f_lseek(&infile, 0);

        if(Res) {
       //     printf("\ncan't wirte file %s!\n", restore_filename);
            skipped++;
            return XST_FAILURE;
        }

        Res = f_write(&infile, (void*)writer.buffer, writer.index, &NumBytesWritten);

        if(Res) {
       //     printf("\ncan't wirte file %s!\n", restore_filename);
            skipped++;
            return XST_FAILURE;
        } else {
       //     printf("\nSuccessfully wirte file %s, size is %dByte!\n", restore_filename, writer.index);
        }

        f_close(&infile);

        /***********************decompress function end*********************/

      //  printf("file %s: %u bytes --> %u bytes, %.2f%%\n", compress_filename, reader.size, writer.index,
      //      writer.index * 100.0 / reader.size);

        tests++;

    }while(!tests);

    free(writer.buffer);
    free(file_buffer);

//    if(errors)
     //   printf("\n***** %d errors detected in %d tests using %d files (%d skipped) *****\n\n", errors, tests, checked, skipped);
//    else {
     //   printf("\nSuccessfully ran %d tests using %d files (%d skipped) with no errors detected\n", tests, checked, skipped);
 //s   }

    return errors;
}
